package com.glodon.paas.web.filter;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

import com.glodon.paas.consts.StringConst;
import com.glodon.paas.exception.PaasServerExceptionFactory;

/**
 * @author wang chen
 */

public class InternalAPIFilter implements Filter{
	private Logger logger = LoggerFactory.getLogger(InternalAPIFilter.class);

    @Value("${api.internal.allow.ips}")
    private String allowIPs;
    
//    @Value("${api.internal.allow.regex}")
//    private String matchTemplete;
    
    private Set<String> ips=null;
    
    private Set<String> ipSegments=null;
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    	ips = new HashSet<String>();
    	ipSegments = new HashSet<String>();
    	if(StringUtils.isNotBlank(allowIPs)){
    		ips.clear();
    		ipSegments.clear();
    		String[] ipss = allowIPs.split(StringConst.SEPARATOR_COMMA);
    		for(String ip : ipss){
    			ip = ip.trim();
    			if(ip.indexOf('-')>0){
    				ipSegments.add(ip);
    			}
    			else{
    				ips.add(ip);
    			}
    		}
    	}
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
            ServletException {
    	HttpServletRequest httpReq = (HttpServletRequest) request;
    	if(ips == null || ipSegments==null){
        	init(null);
        }
    	String rip = httpReq.getHeader(StringConst.X_FORWARDED_FOR);
    	if(StringUtils.isBlank(rip)){
    		rip = httpReq.getRemoteAddr();
    	}
        if (StringUtils.isBlank(rip)) {
            throw PaasServerExceptionFactory.accessForbidden("Can not find the remote IP address, No privilege to access internal API.", null);
        }
        String[] rips = rip.split(StringConst.SEPARATOR_COMMA);
        boolean pass = false;
        for(String ip : rips){
        	if(!pass && !ips.isEmpty()){
        		pass = ips.contains(ip);
//        		if(!res){
//        			logger.debug("{} not contain ip:{}", ips, ip);
//        		}
        	}

        	if(!pass && !ipSegments.isEmpty()){
        		for(String ipSegment : ipSegments){
        			if(ipIsValid(ipSegment, ip)){
        				pass = true;
        				break;
        			}
        		}
        	}
//        	if(res && StringUtils.isNotBlank(matchTemplete)){
//        		Pattern p = Pattern.compile(matchTemplete);
//        		res = p.matcher(ip).find();
//        		if(!res){
//        			logger.debug("Regular expression {} can't match ip:{}", matchTemplete, ip);
//        		}
//        	}
        	
        	if(!pass){
        		logger.warn("IP {} has no privilege to access internal API",ip);
        		break;
        	}
        }
        if (pass) {
            chain.doFilter(request, response);
        } else {
            logger.warn("{} can't contains ip:{}",allowIPs, Arrays.asList(rips).toString());
            throw PaasServerExceptionFactory.accessForbidden("IP:" + Arrays.asList(rips).toString() + " has no privilege to access internal API", null);
        }
    }
    
    @Override
    public void destroy(){
    	
    }
    private boolean ipIsValid(String ipSection, String ip) {  
    	try{
    		int idx = ipSection.indexOf('-');  
    		String[] sips = ipSection.substring(0, idx).split("\\.");  
    		String[] sipe = ipSection.substring(idx + 1).split("\\.");  
    		String[] sipt = ip.split("\\.");  
    		long ips = 0L, ipe = 0L, ipt = 0L;  
    		for (int i = 0; i < 4; ++i) {  
    			ips = ips << 8 | Integer.parseInt(sips[i]);  
    			ipe = ipe << 8 | Integer.parseInt(sipe[i]);  
    			ipt = ipt << 8 | Integer.parseInt(sipt[i]);  
    		}  
    		if (ips > ipe) {  
    			long t = ips;  
    			ips = ipe;  
    			ipe = t;  
    		}  
    		return ips <= ipt && ipt <= ipe;  
    	}
    	catch (Exception e) {
			return false;
		}
    }
    
}
